package org.oschina.helper.query.bak.impl;

import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.oschina.helper.cache.ICacheHelper;
import org.oschina.helper.cache.ICachePipeline;
import org.oschina.helper.db.IDBHelper;
import org.oschina.helper.query.BaseQuery;
import org.oschina.helper.query.IQueryCacheCallback;
import org.oschina.helper.query.bak.IQueryHelper_bak;
import org.oschina.util.ReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

/**
 * 数据库操作实现类 
 * 	JDBCTemplate
 * 	Druid连接池
 */

@Repository
public class QueryHelperImpl_bak extends BaseQuery implements IQueryHelper_bak {

	private final static Logger logger=LoggerFactory.getLogger(QueryHelperImpl_bak.class);
	
	@Autowired
	private IDBHelper dbHelper;
	@Autowired
	private ICacheHelper cacheHelper;
	
	@Override
	public long queryDataSum(String tableName, String[] where, Object[] args)
	{
		Object result=queryForObject(getSumSql(tableName,where),args,Object.class);
		return Long.valueOf(result.toString());
	}

	@Override
	public long queryDataSum_cache(String key,int timeout, String tableName,String[] where, Object[] args)
	{
		String sqlKey=getKey(key,sqlParser(getSumSql(tableName,where),args));
		Object result=cacheHelper.get(sqlKey,Object.class);
		if(result==null)
		{
			result=queryForObject(getSumSql(tableName,where),args,Object.class);
			
			ICachePipeline pipline=cacheHelper.getPipeline();
			pipline.lpush(key, sqlKey, timeout);
			pipline.set(sqlKey, result, timeout);
			pipline.sync();
		}
		return Long.valueOf(result.toString());
	}
	
	@Override
	public int delete(String tableName, String[] where, Object[] args)
	{
		return update(StringUtils.join(new String[]{"DELETE FROM ",tableName,getWhereSql(where)}), args);
	}

	@Override
	public int delete_cache(String key, String tableName, String[] where,Object[] args)
	{
		int result=delete(tableName,where,args);
		if(result!=0)
			del(key);
		return result;
	}

	@Override
	public long insert(String tableName, String[] column, Object[] args)
	{
		return update(StringUtils.join(new String[]{"INSERT INTO ",tableName,getInsertColumnSql(column)}),args);
	}

	@Override
	public long insert_cache(String key,String tableName, String[] column, Object[] args)
	{
		del(key);
		return insert(tableName,column,args);
	}
	
	@Override
	public <T> List<T> query(String sql, Object[] args, Class<T> type)
	{
		return _query(sql, args, type);
	}

	@Override
	public <T> List<T> query(String tableName, String[] columns,String[] where,String sort[], Object[] args, Class<T> type, int first, int max)
	{
		return query(getSelectSql(tableName,columns,where,sort,args,type,first,max),args,type);
	}

	@Override
	public <T> Object queryForObject(String sql, Object[] args, Class<T> type)
	{
		if(ReflectUtil.isBasic(type))
			return _queryForObject(sql, args, type);
		else
			return _queryForBean(sql, args, type);
	}

	@Override
	public <T> Object queryForObject_cache(String key, int timeout,String sql, Object[] args, Class<T> type)
	{
		String sqlKey=getKey(key,sqlParser(sql, args));
		
		if(ReflectUtil.isBasic(type))
		{
			Object result=cacheHelper.get(sqlKey, type);
			if(result==null)
			{
				result=queryForObject(sql, args, type);
				
				ICachePipeline pipline=cacheHelper.getPipeline();
				pipline.lpush(key, sqlKey, timeout);
				pipline.set(sqlKey, result, timeout);
				pipline.sync();
			}
			
			return result;
		}
		else
		{
			Object id=cacheHelper.get(sqlKey, Object.class);
			
			if(id!=null && Integer.parseInt(id.toString())==-1)
				return null;
			
			String idKey=getKey(key,id);
			Object value=(id==null?null:cacheHelper.hget(idKey, type));
			
			if(value==null)
			{
				Object cache=queryForObject(sql, args, type);
				
				ICachePipeline pipline=cacheHelper.getPipeline();
				pipline.lpush(key, sqlKey, timeout);
				pipline.lpush(key, idKey, timeout);
				pipline.set(sqlKey, getId(cache), timeout);
				if(!cacheHelper.exists(idKey))
					pipline.hset(idKey, cache, type, timeout);
				pipline.sync();
				
				value=cache;
			}
			
			return value;
		}
	}
	
	/**
	 * 缓存策略是存ID  然后按照ID挨个获取
	 */
	@Override
	public <T> List<T> query_cache(String key, int timeout, String sql,Object[] args, Class<T> type)
	{
		String sqlKey=getKey(key,sqlParser(sql, args));
		return query_cache(sqlKey,key,timeout,sql,args,type);
	}

	@Override
	public <T> List<T> query_cache(String key,String tableName, int timeout,String[] columns, String[] where,String sort[], Object[] args, Class<T> type,int first, int max)
	{
		return query_cache(key,timeout,getSelectSql(tableName, columns, where, sort, args, type, first, max),args,type);
	}
	
	@Override
	public <T> List<T> query_cache(String cacheKey, String itemKey, String tableName, int timeout, String[] columns, String[] where, String[] sort, Object[] args, Class<T> type, int first, int max)
	{
		return query_cache(cacheKey,itemKey,timeout,getSelectSql(tableName, columns, where, sort, args, type, first, max),args,type);
	}
	
	@Override
	public <T> List<T> query_cache(String cacheKey, String itemKey, int timeout, String sql, Object[] args, Class<T> type)
	{
		if(!cacheHelper.exists(cacheKey))
		{
			List<T> result=query(sql, args, type);
			List<QueryCacheIdBean> ids=new ArrayList<QueryCacheIdBean>();
			ICachePipeline existPipline=cacheHelper.getPipeline();
			for(T temp:result)
			{
				Object id=getId(temp);
				String idKey=getKey(itemKey,id);
				ids.add(new QueryCacheIdBean(id,idKey));
				existPipline.exists(idKey);
			}
			List<Boolean> exists=existPipline.syncAndReturnAll(Boolean.class);
			
			ICachePipeline pushPipline=cacheHelper.getPipeline();
			
			//防止空内容
			pushPipline.rpush(cacheKey,-1,timeout);
			
			for(int i=0,len=exists.size();i<len;i++)
			{
				QueryCacheIdBean queryCacheIdBean=ids.get(i);
				pushPipline.rpush(cacheKey, queryCacheIdBean.getId(), timeout);
				pushPipline.lpush(itemKey,queryCacheIdBean.getIdKey(),timeout);
				if(!exists.get(i))
					pushPipline.hset(queryCacheIdBean.getIdKey(), result.get(i), type, timeout);
			}
			pushPipline.lpush(itemKey,cacheKey,timeout);
			pushPipline.sync();
		}
		List<Object> cache=(List<Object>)cacheHelper.lget(cacheKey, 0,-1,Object.class);
		List<T> result=new ArrayList<T>();
		ICachePipeline pipeline=cacheHelper.getPipeline();
		for(Object id:cache)
			result.add((T)cacheHelper.hget(getKey(itemKey, id), type));
		return result;
	}
	
	@Override
	public <T>List<T> query_cache(String cacheKey,String itemKey,int timeout,int maxLength,int first,int max,Class<T> type,IQueryCacheCallback callback)
	{
		if(maxLength!=-1 && maxCacheCount(first, max, maxLength))
			return callback.get(first, max);
		
		int length=first+max;
		Integer firstValue=(Integer) cacheHelper.lget(cacheKey, first, Integer.class);
		if(firstValue==null || firstValue==-1)
		{
			if(firstValue==null)
			{
				ICachePipeline pipeline=cacheHelper.getPipeline();
				int cacheLength=(int) cacheHelper.llen(cacheKey);
				if(length>cacheLength)
				{
					for(int i=0,len=length-cacheLength;i<len;i++)
						pipeline.rpush(cacheKey, -1, timeout);
				}
				pipeline.sync();
			}
			
			ICachePipeline pipeline=cacheHelper.getPipeline();
			List resultList=callback.get(first,max);
			if(resultList!=null && !resultList.isEmpty())
			{
				List<QueryCacheIdBean> ids=new ArrayList<QueryCacheIdBean>();
				ICachePipeline existPipline=cacheHelper.getPipeline();
				for(Object result:resultList)
				{
					Object id=getId(result);
					String idKey=getKey(itemKey,id);
					ids.add(new QueryCacheIdBean(id,idKey));
					existPipline.exists(idKey);
					
				}
				List<Boolean> exists=existPipline.syncAndReturnAll(Boolean.class);
				
				for(int i=0,len=resultList.size();i<len;i++)
				{
					QueryCacheIdBean queryCacheIdBean=ids.get(i);
					pipeline.lset(cacheKey, first+i, queryCacheIdBean.getId(), timeout);
					if(!exists.get(i))
						pipeline.hset(queryCacheIdBean.getIdKey(), resultList.get(i), type, timeout);
				}
			}
			else
			{
				for(int i=0,len=max;i<len;i++)
					pipeline.rpush(cacheKey, -1, timeout);
			}
			pipeline.sync();
		}
		
		List<Integer> ids=cacheHelper.lget(cacheKey,first,first+max, Integer.class);
		if(ids==null || ids.isEmpty())
			return new ArrayList();
		
		List<T> result=new ArrayList<T>();
		for(Integer id:ids)
		{
			if(id!=-1)
				result.add((T)cacheHelper.hget(getKey(itemKey, id),type));
		}
		return result;
	}

	@Override
	public <T> Object queryForObject(String tableName, String[] columns,String[] where, Object[] args, Class<T> type)
	{
		return queryForObject(getSelectSql(tableName, columns, where, null, args, type, -1, -1),args, type);
	}
	
	@Override
	public <T> Object queryForObject_cache(String key,int timeout,String tableName, String[] columns,String[] where, Object[] args, Class<T> type)
	{
		return queryForObject_cache(key, timeout, getSelectSql(tableName, columns, where, null, args, type, -1, -1), args, type);
	}

	@Override
	public int update(String sql, Object... args)
	{
		return _update(sql,args);
	}

	@Override
	public int update(String tableName, String[] columns, String[] where,Object[] args)
	{
		return update(StringUtils.join(new String[]{"UPDATE ",tableName,getUpdateColumnSql(columns),getWhereSql(where)}),args);
	}

	@Override
	public int update_cache(String key, String sql, Object... args)
	{
		int result=update(sql, args);
		
		if(result!=0)
			del(key);
		
		return result;
	}

	@Override
	public int update_cache(String key,String tableName, String[] columns,String[] where,Object args[])
	{
		int result=update(tableName,columns,where,args);
		
		if(result!=0)
			del(key);
		
		return result;
	}
	
	@Override
	public int[] batchUpdate(String sql, List<Object[]> args)
	{
		return _batchUpdate(sql, args);
	}
	
	@Override
	public int[] batchUpdate_cache(String key,String sql,List<Object[]> args)
	{
		int result[]=batchUpdate(sql,args);
		for(int temp:result)
		{
			if(temp!=0)
			{
				del(key);
				break;
			}
		}
		return result;
	}
	
	private void del(String key)
	{
		List<String> keys=cacheHelper.lget(key, 0, -1, String.class);
		if(keys==null || keys.isEmpty())
			return;
		
		ICachePipeline pipline=cacheHelper.getPipeline();
		for(String lkey:keys)
			pipline.del(lkey);
		pipline.del(key);
		pipline.sync();
	}
	
	private int[] _batchUpdate(String sql,List<Object[]> args)
	{
		return dbHelper.batchUpdate(sql, args);
	}
	
	private Object _queryForBean(String sql,Object args[],Class type)
	{
		return dbHelper.queryForObject(sql, args, type);
	}

	private Object _queryForObject(String sql,Object args[],Class type)
	{
		return dbHelper.queryForObject(sql, args, type);
	}
	
	private List _query(String sql,Object args[],Class propertyType)
	{
		return dbHelper.query(sql, args, propertyType);
	}
	
	private int _update(final String sql,final Object args[])
	{
		return (int)dbHelper.update(sql, args);
	}
}

class QueryCacheIdBean
{
	private Object id;
	private String idKey;
	
	public QueryCacheIdBean(Object id,String idKey)
	{
		this.id=id;
		this.idKey=idKey;
	}
	
	public Object getId() {
		return id;
	}
	public String getIdKey() {
		return idKey;
	}
	public void setId(Object id) {
		this.id = id;
	}
	public void setIdKey(String idKey) {
		this.idKey = idKey;
	}
}